package edu.cicese.alz;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.LookupPaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.xy.DefaultXYZDataset;
import org.jfree.data.xy.XYZDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.openstreetmap.gui.jmapviewer.MapMarkerDot;

import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;

public class GPSChart extends ApplicationFrame {
	private double[][] homeLocations = new double[][]{
			{31.846471255004094, -116.59954458475113},
			{31.84831221834474, -116.57384902238846},
			{31.852568166189663, -116.57005369663239},
			{31.846291257854126, -116.57545566558838},
			{31.846017843789788, -116.57552003860474}, //5
			{31.84652365917161, -116.59621596336365},
			{31.840793201018954, -116.60068988800049},
			{31.837425393948287, -116.59893035888672},
			{31.8421512264652, -116.60020709037781},
			{31.8421512264652, -116.60020709037781}, //10
			{31.84856512009154, -116.59083276987076},
			{31.832348386162064, -116.59635007381439},
			{31.868006859116882, -116.5931636095047},
			{31, -116},
			{31.77313096164925, -116.58813714981079}
	};
	private int radius = 200;
	private int userID = 0;
	private double lat, lon;
	private boolean complete;
	private int periodMs, msElapsed = 0;

	public GPSChart(ResultSet resultSet, int userID, boolean complete) {
		super("GPS Chart: " + userID);

		this.userID = userID;
		this.complete = complete;
		lat = homeLocations[userID - 1][0];
		lon = homeLocations[userID - 1][1];
		JPanel localJPanel = new ChartPanel(createChart(createDataset(resultSet)));
		localJPanel.setPreferredSize(new Dimension(1500, 750));
		setContentPane(localJPanel);
	}

	private XYZDataset createDataset(ResultSet resultSet) {
		DefaultXYZDataset localDefaultXYZDataset = new DefaultXYZDataset();

		ArrayList<MapMarkerDot> markers = new ArrayList<>();
		try {
//			if (userID == 6) {
//			// Correct data
//				while (resultSet.next()) {
//					long timestamp = resultSet.getLong(4);
//					timestamp = timestamp < 1325462463000l ? timestamp - 86400000 : timestamp - 172800000;
//					markers.add(new MapMarkerDot(resultSet.getDouble(2), resultSet.getDouble(3), timestamp));
//				}
//			}
//			else {
				while (resultSet.next()) {
					markers.add(new MapMarkerDot(resultSet.getDouble(2), resultSet.getDouble(3), resultSet.getLong(4)));
				}
//			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

//		if (!markers.isEmpty()) {
		int periods = 144;
		long d1, d2;

		if (complete) {
			d1 = TimeUtil.getDateInMs(DataUtil.dates[userID - 1][0] + " 00:00:00");
			d2 = TimeUtil.getDateInMs(DataUtil.dates[userID - 1][1] + " 24:00:00") - 1;
//			if (userID == 6) {
//				d2 = TimeUtil.getDateInMs("3/1/2012 24:00:00") - 1;
//			}
		} else {
			d1 = markers.get(0).getTimestamp();
			d2 = markers.get(markers.size() - 1).getTimestamp();
		}

		int numberOfDays = TimeUtil.getDayDifference(d1, d2);
		int dataSize = periods * numberOfDays;

//			System.out.println(numberOfDays + " " + markers.size());
//			System.out.println(d1 + "  " + d2);
//			double[] days = new double[numberOfDays];

		Calendar cal = TimeUtil.getDayTimestamp(d1);
		long tsAux = cal.getTimeInMillis();
		long tsBegin = tsAux;
		long tsMarker;

		if (!markers.isEmpty()) {
			tsMarker = markers.get(0).getTimestamp();
			markers.remove(0);
		} else {
			tsMarker = 2000000000000l;
		}
		int periodMs = 600000;

		double[] xValues = new double[dataSize];
		double[] yValues = new double[dataSize];
		double[] zValues = new double[dataSize];

		double value;
		MapMarkerDot marker = null;

		for (int day = 0; day < numberOfDays; day++) {
//				if (day <= numberOfDays - 1) {
//					System.out.println(markers.size());
//				}
			for (int period = 0; period < periods; period++) {
				if (!markers.isEmpty()) {
					while (tsAux > tsMarker) {
						marker = markers.get(0);
						tsMarker = marker.getTimestamp();
						markers.remove(0);
						if (markers.isEmpty()) {
							break;
						}
					}

					// remove if?
					if (tsMarker - tsAux < periodMs) {
						value = marker != null && isHome(marker.getLat(), marker.getLon()) ? 2.5 : 0.5;
						xValues[(day * periods + period)] = tsBegin;
						yValues[(day * periods + period)] = period;
						zValues[(day * periods + period)] = value;
						tsAux += periodMs;
					} else {
						while (tsMarker - tsAux > periodMs) {
							xValues[(day * periods + period)] = tsBegin;
							yValues[(day * periods + period)] = period;
							zValues[(day * periods + period)] = 1.5;
							period++;
							tsAux += periodMs;
						}
						period--;
					}
				} else {
					xValues[(day * periods + period)] = tsBegin;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = 0;
					tsAux += periodMs;
				}
			}

			cal.add(Calendar.DAY_OF_MONTH, 1);
			tsBegin = tsAux = cal.getTimeInMillis();
		}


		//TODO THE LAST MARKER SEEMS TO BE MISSING.


/*			Object localObject = new Day();

//			((RegularTimePeriod) localObject).
			for (int day = 0; day < numberOfDays; day++) {
				double d = 1.0D;
				for (int period = 0; period < periods; period++) {
					if (Math.random() < 0.1D)
						d = Math.random() * 2.0D;
					xValues[(day * periods + period)] = ((RegularTimePeriod) localObject).getFirstMillisecond();
					xValues[(day * periods + period)] = day;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = d;
				}
//				localObject = ((RegularTimePeriod) localObject).next();
			}*/

		localDefaultXYZDataset.addSeries("Series 1", new double[][]{xValues, yValues, zValues});
//		}

		return localDefaultXYZDataset;
	}

	private XYZDataset createDatasetRemovingOutliers(ResultSet resultSet) {
		DefaultXYZDataset localDefaultXYZDataset = new DefaultXYZDataset();

		ArrayList<MapMarkerDot> markers = new ArrayList<>();
		try {
			while (resultSet.next()) {
				markers.add(new MapMarkerDot(resultSet.getDouble(2), resultSet.getDouble(3), resultSet.getLong(4)));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		int periods = 144;
		long d1, d2;

		if (complete) {
			d1 = TimeUtil.getDateInMs(DataUtil.dates[userID - 1][0] + " 00:00:00");
			d2 = TimeUtil.getDateInMs(DataUtil.dates[userID - 1][1] + " 24:00:00") - 1;
		} else {
			d1 = markers.get(0).getTimestamp();
			d2 = markers.get(markers.size() - 1).getTimestamp();
		}

		int numberOfDays = TimeUtil.getDayDifference(d1, d2);
		int dataSize = periods * numberOfDays;

		double[] xValues = new double[dataSize];
		double[] yValues = new double[dataSize];
		double[] zValues = new double[dataSize];

		Calendar cal = TimeUtil.getDayTimestamp(d1);
		long tsAux = cal.getTimeInMillis();
		long tsBegin = tsAux;
		long tsMarker;

		if (!markers.isEmpty()) {
			tsMarker = markers.get(0).getTimestamp();
			markers.remove(0);
		} else {
			tsMarker = 2000000000000l;
		}
		periodMs = 600000;

		double value;
		MapMarkerDot marker = null;

		for (int day = 0; day < numberOfDays; day++) {

			for (int period = 0; period < periods; period++) {

				if (!markers.isEmpty()) {

					xValues[(day * periods + period)] = tsBegin;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = getValue(markers, tsAux);

					tsAux += periodMs;

					/*while (tsAux > tsMarker) {
						marker = markers.get(0);
						tsMarker = marker.getTimestamp();
						markers.remove(0);
						if (markers.isEmpty()) {
							break;
						}
					}

					// remove if?
					if (tsMarker - tsAux < periodMs) {
						value = marker != null && isHome(marker.getLat(), marker.getLon()) ? 2.5 : 0.5;
						xValues[(day * periods + period)] = tsBegin;
						yValues[(day * periods + period)] = period;
						zValues[(day * periods + period)] = value;
						tsAux += periodMs;
					} else {
						while (tsMarker - tsAux > periodMs) {
							xValues[(day * periods + period)] = tsBegin;
							yValues[(day * periods + period)] = period;
							zValues[(day * periods + period)] = 1.5;
							period++;
							tsAux += periodMs;
						}
						period--;
					}*/
				} else {
					xValues[(day * periods + period)] = tsBegin;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = 0;
					tsAux += periodMs;
				}
			}

			cal.add(Calendar.DAY_OF_MONTH, 1);
			tsBegin = tsAux = cal.getTimeInMillis();
		}

		localDefaultXYZDataset.addSeries("Series 1", new double[][]{xValues, yValues, zValues});
		return localDefaultXYZDataset;
	}

	private JFreeChart createChart(XYZDataset xyzDataset) {
		DateAxis localDateAxis = new DateAxis("Date");
		localDateAxis.setLowerMargin(0.0D);
		localDateAxis.setUpperMargin(0.0D);
		localDateAxis.setInverted(true);

//		DateAxis localNumberAxis = new DateAxis("Hour");
//		localNumberAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MINUTE, 10));
//		DateFormat chartFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm");
//		localNumberAxis.setDateFormatOverride(chartFormatter);

		NumberAxis localNumberAxis = new NumberAxis("Hour");
//		DateAxis localNumberAxis = new DateAxis("Hour");
		localNumberAxis.setUpperMargin(0.0D);
		localNumberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
//		localNumberAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MINUTE, 1));

		XYBlockRenderer localXYBlockRenderer = new XYBlockRenderer();
		localXYBlockRenderer.setBlockWidth(86400000.0D);
		localXYBlockRenderer.setBlockAnchor(RectangleAnchor.BOTTOM_LEFT);
		LookupPaintScale localLookupPaintScale = new LookupPaintScale(0.5, 3.5, Color.RED);
		localLookupPaintScale.add(0.5, Color.GREEN);
		localLookupPaintScale.add(1.5, Color.RED);
		localLookupPaintScale.add(2.5, Color.BLUE);
//    localLookupPaintScale.add(3.5D, Color.YELLOW);
		localXYBlockRenderer.setPaintScale(localLookupPaintScale);

		XYPlot localXYPlot = new XYPlot(xyzDataset, localDateAxis, localNumberAxis, localXYBlockRenderer);
		localXYPlot.setOrientation(PlotOrientation.HORIZONTAL);
		localXYPlot.setBackgroundPaint(Color.WHITE);
		localXYPlot.setRangeGridlinePaint(Color.WHITE);
		localXYPlot.setAxisOffset(new RectangleInsets(5.0D, 5.0D, 5.0D, 5.0D));

		JFreeChart localJFreeChart = new JFreeChart("GPS Chart: " + userID, localXYPlot);
		localJFreeChart.removeLegend();
		localJFreeChart.setBackgroundPaint(Color.WHITE);

		SymbolAxis localSymbolAxis = new SymbolAxis(null, new String[]{"", "Out", "No Data", "Home", "Group 2"});
		localSymbolAxis.setRange(0.5D, 4.5D);
		localSymbolAxis.setPlot(new PiePlot());
		localSymbolAxis.setGridBandsVisible(false);
		PaintScaleLegend localPaintScaleLegend = new PaintScaleLegend(localLookupPaintScale, localSymbolAxis);
		localPaintScaleLegend.setMargin(new RectangleInsets(3.0D, 10.0D, 3.0D, 10.0D));
		localPaintScaleLegend.setPosition(RectangleEdge.BOTTOM);
		localPaintScaleLegend.setAxisOffset(5.0D);
		localJFreeChart.addSubtitle(localPaintScaleLegend);
		ChartUtilities.applyCurrentTheme(localJFreeChart);
		return localJFreeChart;
	}

	private boolean isHome(double lat, double lon) {
		return GPSUtilities.getSLCDistanceFrom(this.lat, this.lon, lat, lon) <= radius;
	}

	private double getValue(ArrayList<MapMarkerDot> markers, long tsAux) {
		long tsA = tsAux;
		long tsB = tsAux + periodMs;

		double value = 0.5;
		long mTimestamp;
		boolean moved = false;

		while (!markers.isEmpty()) {

			mTimestamp = markers.get(0).getTimestamp();

			if (mTimestamp > tsB) {
				break;
			}

			/*if (mTimestamp > tsA && mTimestamp <= tsB) {
//				System.out.println("in");
				if (moved(markers.get(0).getMagnitude())) {
					value = 2.5;
					moved = true;
				}
				else if (!moved) {
//					System.out.println("no movement");
					value = 1.5;
				}
			}*/

			markers.remove(0);
		}

		msElapsed += periodMs;

		return value;
	}

//	public static void main(String[] paramArrayOfString) {
//		GPSChart gpsChart = new GPSChart("GPS Chart");
//		gpsChart.pack();
//		RefineryUtilities.centerFrameOnScreen(gpsChart);
//		gpsChart.setVisible(true);
//	}
}